package spaska.data.readers;

import java.io.BufferedReader;
import java.io.EOFException;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.LinkedHashSet;

import spaska.data.Attribute;
import spaska.data.Attribute.ValueType;
import spaska.data.Dataset;
import spaska.data.Factory;
import spaska.data.Instance;
import spaska.data.Value;

/**
 * ARFF reader.
 * 
 * @author <a href="mailto:vesko.m.georgiev@gmail.com">Vesko Georgiev<a>
 */
public class ARFFReader extends AbstractDataReader {

	private static final String TAG_RELATION = "@relation";
	private static final String TAG_ATTRIBUT = "@attribute";
	private static final String TAG_DATA = "@data";

	private File file;

	public ARFFReader(String file) {
		this(new File(file));
	}

	public ARFFReader(File file) {
		this.file = file;
	}

	@Override
	public Dataset buildDataset() {
		Dataset dataset = new Dataset();
		BufferedReader input = null;
		try {
			input = new BufferedReader(new InputStreamReader(new FileInputStream(file)));

			String line = null;
			while ((line = input.readLine()) != null) {
				if (line.toLowerCase().startsWith(TAG_RELATION)) {
					line = line.substring(TAG_RELATION.length() + 1).trim();
					dataset.setName(line);
				}
				else if (line.toLowerCase().startsWith(TAG_ATTRIBUT)) {
					handleAttribute(dataset, line);
				}
				else if (line.toLowerCase().startsWith(TAG_DATA)) {
					handleData(dataset, input);
				}
			}
		}
		catch (EOFException e) {
			// do nothing
		}
		catch (Exception e) {
			e.printStackTrace();
		}
		finally {
			if (input != null) {
				try {
					input.close();
				}
				catch (IOException e) {
				}
			}
		}
		dataset.setClassIndex(dataset.getAttributesCount() - 1);
		for (Validator v : validators) {
			v.validate(dataset);
		}
		return dataset;
	}

	private ValueType getValueType(String type) {
		type = type.toLowerCase();
		if (type.equals("real") || type.equals("numeric") || type.equals("integer")) {
			return ValueType.Numeric;
		}
		else if (type.startsWith("{") || type.equals("text") || type.equals("string")) {
			return ValueType.Nominal;
		}
		return ValueType.Unknown;
	}

	private void handleAttribute(Dataset dataset, String line) {
		String[] token = line.split("\\s+");

		if (token.length > 2) {
			String name = token[1];
			ValueType type = getValueType(token[2].toLowerCase());

			Attribute attr = new Attribute(name, type);
			dataset.addAttribute(attr);

			if (type == ValueType.Nominal) {
				LinkedHashSet<Value> domain = new LinkedHashSet<Value>();
				int start = line.indexOf("{");
				int end = line.indexOf("}", start + 1);
				String[] domains = line.substring(start + 1, end).split(",");

				for (String str : domains) {
					domain.add(Factory.createValue(str.trim()));
				}
				dataset.setAttributeDomain(attr, domain);
			}
		}
	}

	private void handleData(Dataset dataset, BufferedReader input) throws IOException {
		String line = null;
		while ((line = input.readLine()) != null) {
			String trim = line.trim();
			if (!trim.equals("") && !trim.startsWith("%")) {
				String[] strValues = line.split(",");
				Value[] element = Factory.createValues(strValues, dataset);
				dataset.addElement(new Instance(element));
			}
		}
	}

}
